//
// Created by martin on 3/22/22.
//

#ifndef MUDUO_FTPSERVER_H
#define MUDUO_FTPSERVER_H

#include "muduo/net/TcpServer.h"
#include "muduo/base/noncopyable.h"
#include "muduo/base/Mutex.h"
#include "IdleConnectionEntry.h"
#include "UserDao.h"

#include <string>
#include <atomic>
#include <memory>
#include <unordered_set>
#include <set>
#include <boost/circular_buffer.hpp>

namespace ftp
{

class FtpRequest;
class FtpResponse;

/**
 * @brief simple Ftp Server
 *
 * conform to RFC 959 (not support all command, partially to RFC 3659)
 *
 * Using the Ftp Server is very simple. Code will look like this:
 *
 * @code{.cpp}
 *
 * muduo::net::EventLoop loop;
 * muduo::net::InetAddress address(2008);
 * ftp::FtpServer server(&loop, address);
 * server.addAnonymous(Permission::ReadOnly);
 * server.addUser("root", "123465", Permission::All);
 * server.setThreadNum(4);
 * server.start();
 *
 * @endcode
 *
 * @see
 * https://www.rfc-editor.org/rfc/rfc959.html
 * https://www.rfc-editor.org/rfc/rfc3659.html
 */
class FtpServer : muduo::noncopyable
{
public:
	typedef std::function<void(const FtpRequest&, FtpResponse*)> FtpCallback;

	FtpServer(muduo::net::EventLoop* loop,
		const muduo::net::InetAddress& listenAddr,
		const std::string& name = "MyFtpServer",
		muduo::net::TcpServer::Option option = muduo::net::TcpServer::kNoReusePort);

	//------------------------------------------------------
	// Reactor
	//------------------------------------------------------

	// Start Ftp server
	void start();

	// Set sub-thread number for thread pool of TcpServer.
	// Note: one loop per thread
	void setThreadNum(int numThreads)
	{
		server_.setThreadNum(numThreads);
	}

	// Get loop which the server belongs to
	muduo::net::EventLoop* getLoop()
	{
		return server_.getLoop();
	}

	//------------------------------------------------------
	// Ftp User
	//------------------------------------------------------

	// Add ftp user
	bool addUser(const std::string& username, const std::string& password, const std::string& localRootPath, Permission permission);
	// Add anonymous user
	bool addAnonymousUser(const std::string& localRootPath, Permission permission);

	//------------------------------------------------------
	// Control Connection
	//------------------------------------------------------

	// Get numbers of open connection (control connection)
	int getNumsOpenConnection() const;

	// Cache numbers of open connection (control connection)
	static int cachedNumsOpenConnectionMax();

	// Threshold value: max limit of numbers of open connection (control connection)
	static int numsOpenConnectionMax_;

private:
	//------------------------------------------------------
	// Control Connection Callback
	//------------------------------------------------------

	// When receive create/close control connection request, it will be called back 
	// by the EventLoopThread which owns the control connection (TcpConnection).
	void onConnection(const muduo::net::TcpConnectionPtr& conn);

	// After receiving Ftp command from client by control connection, it will be called back
	// by the EventLoopThread which owns the control connection (TcpConnection).
	void onMessage(const muduo::net::TcpConnectionPtr& conn,
		muduo::net::Buffer* buffer,
		muduo::Timestamp receiveTime);

	// After responsing Ftp message to client by control connection, it will be called back
	// by the EventLoopThread which owns the control connection (TcpConnection).
	void onWriteComplete(const muduo::net::TcpConnectionPtr& conn);

	// After parsing a complete Ftp command to a FtpRequest object, it will be called back in onConnection().
	void onRequest(const muduo::net::TcpConnectionPtr& conn, const FtpRequest& req);

	//------------------------------------------------------
	// Timing Task
	//------------------------------------------------------

	// Timing task for the server. It will be called back when time out.
	void onTimer();
	void dumpConnectionBuckets() const;

	//------------------------------------------------------
	// Control Connection Status
	//------------------------------------------------------

	// Check if current numbers of open connection over max limit(numsOpenConnectionMax_)
	bool overNumsOpenConnectionMax() const;

private:
	typedef std::unordered_set<IdleConnectionEntryPtr> Bucket;
	typedef boost::circular_buffer<Bucket> WeakConnectionList;

	// User interface to operator User database
	UserDao fptUserDao_;

	// TcpServer, one TcpServer per FtpServer
	muduo::net::TcpServer server_;

	muduo::MutexLock mutex_;

	WeakConnectionList connectionBuckets_ GUARDED_BY(mutex_); // 超时连接环形缓冲区

	// Current numbers of open connection
	std::atomic<int> numsOpenConnection_;
};

} // namespace ftp

#endif //MUDUO_FTPSERVER_H
